package net.ankao.job.utils;

import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author LILU
 * @date 2025-01-20 10:14
 */
public class EmailRateLimiterUtils {

    // 存储每个jobId的发送记录
    private static final ConcurrentHashMap<String, JobEmailRecord> jobEmailRecords = new ConcurrentHashMap<>();

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            boolean sendEmail = shouldSendEmail("11", 5);
            if (sendEmail){
                System.out.println("发送");
            }
        }
        boolean sendEmail = shouldSendEmail("11", 6);
        if (sendEmail){
            System.out.println("发送6");
        }
    }

    /**
     * 判断是否应该发送邮件通知
     *
     * @param jobId            任务ID
     * @param maxEmailsPerHour 每小时最大发送次数
     * @return true 如果应该发送邮件通知；否则 false
     */
    public static boolean shouldSendEmail(String jobId, Integer maxEmailsPerHour) {
        synchronized (jobId) {
            LocalDateTime now = LocalDateTime.now();
            AtomicReference<Boolean> shouldSendEmail = new AtomicReference();
            jobEmailRecords.compute(jobId, (id, record) -> {
                if (record == null) {
                    // 第一次为该jobId创建记录
                    record = new JobEmailRecord(now);
                }
                // 更新记录中的最后尝试时间和发送计数
                record.updateLastAttemptTime(now);

                // 检查是否超过了每小时的最大发送次数
                if (record.getEmailCountInLastHour(now) < maxEmailsPerHour) {
                    // 如果没有超过，则增加发送计数并返回true表示可以发送
                    record.incrementEmailCount();
                    shouldSendEmail.set(true);
                    return record;
                } else {
                    // 如果已经超过了最大发送次数，则不增加计数并返回false
                    shouldSendEmail.set(false);
                    return record;
                }
            });
            return shouldSendEmail.get();
        }
    }


    // 内部类用于保存每个jobId的邮件发送状态
    private static class JobEmailRecord {
        private LocalDateTime lastAttemptTime = LocalDateTime.MIN;
        private LocalDateTime firstAttemptTimeInCurrentWindow = LocalDateTime.MIN;
        private int emailCountInCurrentWindow = 0;

        public JobEmailRecord(LocalDateTime now) {
            this.lastAttemptTime = now;
            this.firstAttemptTimeInCurrentWindow = now;
        }

        public void updateLastAttemptTime(LocalDateTime time) {
            this.lastAttemptTime = time;
        }

        public void incrementEmailCount() {
            this.emailCountInCurrentWindow++;
        }

        public int getEmailCountInLastHour(LocalDateTime now) {
            // 如果当前时间距离第一次尝试已经超过一小时，则重置计数
            if (now.isAfter(firstAttemptTimeInCurrentWindow.plusHours(1))) {
                resetEmailCount(now);
            }
            return emailCountInCurrentWindow;
        }

        private void resetEmailCount(LocalDateTime now) {
            this.firstAttemptTimeInCurrentWindow = now;
            this.emailCountInCurrentWindow = 1; // 当前这次也算一次
        }
    }

}
